package com.cauli.file.controller;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cauli.file.component.FileComponent;
import com.cauli.file.component.FileDealComp;
import com.cauli.file.model.dto.file.BatchDeleteFileDTO;
import com.cauli.file.model.dto.file.BatchMoveFileDTO;
import com.cauli.file.model.dto.file.CopyFileDTO;
import com.cauli.file.model.dto.file.CreateFileDTO;
import com.cauli.file.model.dto.file.CreateFoldDTO;
import com.cauli.file.model.dto.file.FileQueryDTO;
import com.cauli.file.model.dto.file.RenameFileDTO;
import com.cauli.file.model.dto.file.SearchFileQueryDTO;
import com.cauli.file.model.dto.file.UnzipFileDTO;
import com.cauli.file.model.dto.file.UpdateFileDTO;
import com.cauli.file.model.entity.FileEntity;
import com.cauli.file.model.entity.UserFile;
import com.cauli.file.model.vo.file.FileDetailVO;
import com.cauli.file.model.vo.file.FileListVO;
import com.cauli.file.model.vo.file.SearchFileVO;
import com.cauli.file.service.FileService;
import com.cauli.file.service.UserFileService;
import com.cauli.file.utils.TreeNode;
import com.cauli.file.utils.UserFileUtils;
import com.cauli.utils.exception.MyException;
import com.cauli.utils.util.ResultResponse;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import com.qiwenshare.common.anno.MyLog;
import com.qiwenshare.common.exception.QiwenException;
import com.qiwenshare.common.result.RestResult;
import com.qiwenshare.ufop.factory.UFOPFactory;
import com.qiwenshare.ufop.operation.copy.Copier;
import com.qiwenshare.ufop.operation.copy.domain.CopyFile;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Parameter;
import org.eclipse.jetty.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.UUID;

/**
 * @author Cauli
 * @date 2023-03-18 12:00
 * @description 文件操作 前端控制器
 */
@Api(tags = "文件操作")
@ApiSort(1)
@RestController
@RequestMapping("/file")
public class FileController {
    @Autowired
    private FileService fileService;

    @Autowired
    private UserFileService userFileService;

    @Resource
    private UFOPFactory ufopFactory;

    @Resource
    private FileDealComp fileDealComp;

    @Value("${ufop.storage-type}")
    private Integer storageType;

    public static final String CURRENT_MODULE = "文件操作";

    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "获取文件列表")
    @GetMapping(value = "/getFileList")
    public ResultResponse<FileListVO> getFileList(FileQueryDTO fileQueryDTO) {
        IPage<FileListVO> fileList;
        if (fileQueryDTO.getFileType() != null && 0 == fileQueryDTO.getFileType()) {
            fileList = userFileService.userFileList(fileQueryDTO);
        } else {
            fileList = userFileService.getFileByFileType(StpUtil.getLoginIdAsLong(), fileQueryDTO);
        }
        return ResultResponse.success().dataList(fileList.getRecords(), fileList.getTotal());
    }

    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "获取文件树")
    @GetMapping("/getFileTree")
    public RestResult<TreeNode> getFileTree() {
        long userId = StpUtil.getLoginIdAsLong();

        RestResult<TreeNode> result = new RestResult<>();

        List<UserFile> userFileList = userFileService.selectFilePathTreeByUserId(userId);
        TreeNode resultTreeNode = new TreeNode();
        resultTreeNode.setLabel(FileComponent.separator);
        resultTreeNode.setId(0L);
        long id = 1;
        for (int i = 0; i < userFileList.size(); i++) {
            UserFile userFile = userFileList.get(i);
            FileComponent fileComponent = new FileComponent(userFile.getFilePath(), userFile.getFileName(), false);
            String filePath = fileComponent.getPath();

            Queue<String> queue = new LinkedList<>();

            String[] strArr = filePath.split(FileComponent.separator);
            for (int j = 0; j < strArr.length; j++) {
                if (!"".equals(strArr[j]) && strArr[j] != null) {
                    queue.add(strArr[j]);
                }
            }
            if (queue.size() == 0) {
                continue;
            }
            resultTreeNode = fileDealComp.insertTreeNode(resultTreeNode, id++, FileComponent.separator, queue);
        }
        List<TreeNode> treeNodeList = resultTreeNode.getChildren();
        Collections.sort(treeNodeList, (o1, o2) -> {
            long i = o1.getId() - o2.getId();
            return (int) i;
        });
        result.setSuccess(true);
        result.setData(resultTreeNode);
        return result;
    }

    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "查询文件详情")
    @GetMapping("/detail")
    public ResultResponse<FileDetailVO> queryFileDetail(@Parameter(description = "用户文件Id", required = true) Long userFileId) {
        FileDetailVO vo = fileService.getFileDetail(userFileId);
        return ResultResponse.success().data(vo);
    }

    @MyLog(operation = "创建文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "创建文件")
    @PostMapping("/createFile")
    public ResultResponse<?> createFile(@Valid @RequestBody CreateFileDTO createFileDTO) {
        try {
            long userId = StpUtil.getLoginIdAsLong();
            String filePath = createFileDTO.getFilePath();
            String fileName = createFileDTO.getFileName();
            String extendName = createFileDTO.getExtendName();

            // 查询是否有同名文件
            List<UserFile> userFiles = userFileService.selectSameUserFile(fileName, filePath, extendName, userId);
            if (userFiles != null && !userFiles.isEmpty()) {
                return ResultResponse.fail().message("同名文件已存在");
            }

            String templateFilePath = "";
            if ("docx".equals(extendName)) {
                templateFilePath = "static/template/Word.docx";
            } else if ("xlsx".equals(extendName)) {
                templateFilePath = "static/template/Excel.xlsx";
            } else if ("pptx".equals(extendName)) {
                templateFilePath = "static/template/PowerPoint.pptx";
            } else if ("txt".equals(extendName)) {
                templateFilePath = "static/template/Text.txt";
            } else if ("drawio".equals(extendName)) {
                templateFilePath = "static/template/Drawio.drawio";
            }
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            String url = ClassUtils.getDefaultClassLoader().getResource(templateFilePath).getPath();

            // 创建文件
            url = URLDecoder.decode(url, "UTF-8");
            FileInputStream fileInputStream = new FileInputStream(url);
            Copier copier = ufopFactory.getCopier();
            CopyFile copyFile = new CopyFile();
            copyFile.setExtendName(extendName);
            String fileUrl = copier.copy(fileInputStream, copyFile);

            // 保存文件信息
            FileEntity fileEntity = new FileEntity();
            fileEntity.setFileSize(0L);
            fileEntity.setFileUrl(fileUrl);
            fileEntity.setStorageType(storageType);
            fileEntity.setIdentifier(uuid);
            fileEntity.setFileStatus(1);
            boolean saveFlag = fileService.save(fileEntity);

            UserFile userFile = new UserFile();
            if (saveFlag) {
                // 保存用户文件关联信息
                userFile.setUserId(userId);
                userFile.setFileName(fileName);
                userFile.setFilePath(filePath);
                userFile.setIsDir(0);
                userFile.setExtendName(extendName);
                userFile.setFileId(fileEntity.getFileId());
                userFileService.save(userFile);
            }
            // ------------------------更新ES数据------------------------
            fileDealComp.uploadES(userFile.getUserFileId());
            return ResultResponse.success().message("文件创建成功");
        } catch (Exception e) {
            return ResultResponse.fail().message(e.getMessage());
        }
    }

    @MyLog(operation = "创建文件夹", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 5)
    @ApiOperation(value = "创建文件夹")
    @PostMapping(value = "/createFold")
    public ResultResponse<?> createFold(@Valid @RequestBody CreateFoldDTO createFoldDTO) {
        long userId = StpUtil.getLoginIdAsLong();
        // 查询是否有同名文件夹
        String filePath = createFoldDTO.getFilePath();
        boolean isDirExist = fileDealComp.isDirExist(createFoldDTO.getFileName(), createFoldDTO.getFilePath(), userId);
        if (isDirExist) {
            throw new MyException("同名文件夹已存在");
        }

        UserFile userFile = UserFileUtils.getUserDir(userId, filePath, createFoldDTO.getFileName());
        userFileService.save(userFile);

        // ------------------------更新ES数据------------------------
        fileDealComp.uploadES(userFile.getUserFileId());
        return ResultResponse.success().message("文件夹创建成功");
    }

    @MyLog(operation = "文件重命名", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 6)
    @ApiOperation(value = "文件重命名")
    @PostMapping(value = "/renameFile")
    public ResultResponse<?> renameFile(@RequestBody RenameFileDTO renameFileDTO) {
        long userId = StpUtil.getLoginIdAsLong();
        // 查询是否有同名文件
        UserFile userFile = userFileService.getById(renameFileDTO.getUserFileId());
        List<UserFile> userFiles = userFileService.selectUserFileByNameAndPath(renameFileDTO.getFileName(), userFile.getFilePath(), userId);
        if (userFiles != null && !userFiles.isEmpty()) {
            throw new MyException("同名文件已存在");
        }

        LambdaUpdateWrapper<UserFile> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(UserFile::getFileName, renameFileDTO.getFileName())
                .eq(UserFile::getUserFileId, renameFileDTO.getUserFileId());
        userFileService.update(userFile, lambdaUpdateWrapper);
        if (1 == userFile.getIsDir()) {
            String path = new FileComponent(userFile.getFilePath(), userFile.getFileName(), true).getPath();
            List<UserFile> userFileList = userFileService.selectUserFileByLikeRightFilePath(path, userId);

            for (UserFile newUserFile : userFileList) {
                String path1 = new FileComponent(userFile.getFilePath(), userFile.getFileName(), userFile.getIsDir() == 1).getPath();
                String path2 = new FileComponent(userFile.getFilePath(), renameFileDTO.getFileName(), userFile.getIsDir() == 1).getPath();
                newUserFile.setFilePath(newUserFile.getFilePath().replaceFirst(path1, path2));
                userFileService.updateById(newUserFile);
            }
        }
        // ------------------------更新ES数据------------------------
        fileDealComp.uploadES(userFile.getUserFileId());
        return ResultResponse.success().message("重命名成功");
    }

    @MyLog(operation = "复制文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 7)
    @ApiOperation(value = "复制文件")
    @PostMapping("/copyFile")
    public ResultResponse<String> copyFile(@RequestBody CopyFileDTO copyFileDTO) {
        long userId = StpUtil.getLoginIdAsLong();
        String filePath = copyFileDTO.getFilePath();
        String userFileIds = copyFileDTO.getUserFileIds();
        String[] userFileIdArr = userFileIds.split(",");
        for (String userFileId : userFileIdArr) {
            UserFile userFile = userFileService.getById(userFileId);
            String oldFilePath = userFile.getFilePath();
            String fileName = userFile.getFileName();
            if (userFile.isDirectory()) {
                FileComponent fileComponent = new FileComponent(oldFilePath, fileName, true);
                if (filePath.startsWith(fileComponent.getPath() + FileComponent.separator) || filePath.equals(fileComponent.getPath())) {
                    return ResultResponse.fail().message("原路径与目标路径冲突，不能复制");
                }
            }
            userFileService.userFileCopy(userId, Long.valueOf(userFileId), filePath);
            fileDealComp.deleteRepeatSubDirFile(filePath, userId);
        }
        return ResultResponse.success().message("复制成功");
    }

    @MyLog(operation = "移动文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 8)
    @ApiOperation(value = "移动文件")
    @PostMapping("/moveFile")
    public ResultResponse<String> moveFile(@RequestBody BatchMoveFileDTO batchMoveFileDto) {
        long userId = StpUtil.getLoginIdAsLong();

        String newFilePath = batchMoveFileDto.getFilePath();

        String userFileIds = batchMoveFileDto.getUserFileIds();
        String[] userFileIdArr = userFileIds.split(",");

        for (String userFileId : userFileIdArr) {
            UserFile userFile = userFileService.getById(userFileId);
            if (StringUtil.isEmpty(userFile.getExtendName())) {
                FileComponent fileComponent = new FileComponent(userFile.getFilePath(), userFile.getFileName(), true);
                if (newFilePath.startsWith(fileComponent.getPath() + FileComponent.separator) || newFilePath.equals(fileComponent.getPath())) {
                    return ResultResponse.fail().message("原路径与目标路径冲突，不能移动");
                }
            }
            userFileService.updateFilepathByUserFileId(userFile.getUserFileId(), newFilePath, userId);
        }
        return ResultResponse.success().message("移动文件成功");
    }

    @MyLog(operation = "删除文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 9)
    @ApiOperation(value = "删除文件")
    @DeleteMapping("/deleteFile")
    public ResultResponse<?> deleteFile(@RequestBody BatchDeleteFileDTO batchDeleteFileDTO) {
        long userId = StpUtil.getLoginIdAsLong();
        String userFileIds = batchDeleteFileDTO.getUserFileIds();
        String[] userFileIdArr = userFileIds.split(",");
        for (String userFileId : userFileIdArr) {
            userFileService.deleteUserFile(Long.valueOf(userFileId), userId);
            // ------------------------删除ES数据------------------------
            fileDealComp.deleteES(Long.valueOf(userFileId));
        }
        return ResultResponse.success().message("删除成功");
    }

    @MyLog(operation = "解压文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 10)
    @ApiOperation(value = "解压文件")
    @PostMapping("/unzipFile")
    public ResultResponse<String> unzipFile(@RequestBody UnzipFileDTO unzipFileDTO) {
        fileService.unzipFile(unzipFileDTO.getUserFileId(), unzipFileDTO.getUnzipMode(), unzipFileDTO.getFilePath());
        return ResultResponse.success().message("解压成功");
    }

    @MyLog(operation = "修改文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 11)
    @ApiOperation(value = "修改文件")
    @PostMapping("/updateFile")
    public ResultResponse<String> updateFile(@RequestBody UpdateFileDTO updateFileDTO) {
        UserFile userFile = userFileService.getById(updateFileDTO.getUserFileId());
        FileEntity fileEntity = fileService.getById(userFile.getFileId());
        Long pointCount = fileService.getFilePointCount(userFile.getFileId());
        String fileUrl = fileEntity.getFileUrl();
        if (pointCount > 1) {
            fileUrl = fileDealComp.copyFile(fileEntity, userFile);
        }
        String content = updateFileDTO.getFileContent();
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(content.getBytes());
        try {
            int fileSize = byteArrayInputStream.available();
            fileDealComp.saveFileInputStream(fileEntity.getStorageType(), fileUrl, byteArrayInputStream);

            String md5Str = fileDealComp.getIdentifierByFile(fileUrl, fileEntity.getStorageType());

            fileService.updateFileDetail(userFile.getUserFileId(), md5Str, fileSize);
        } catch (Exception e) {
            throw new QiwenException(999999, "修改文件异常");
        } finally {
            try {
                byteArrayInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return ResultResponse.success().message("修改文件成功");
    }

    @ApiOperationSupport(order = 12)
    @ApiOperation(value = "根据用户文件ID查询文件")
    @GetMapping("/getUserFile/{userFileId}")
    public ResultResponse<Boolean> getUserFile(@PathVariable Long userFileId) {
        UserFile userFile = userFileService.getById(userFileId);
        if (userFile != null) {
            return ResultResponse.success().data(true);

        } else {
            return ResultResponse.success().data(false);
        }
    }
}
